package org.rcgwt.client.ui.grid;

import org.rcgwt.client.ui.grid.RcGridConfig.IColumnConfig;
import org.rcgwt.client.ui.grid.controller.IGridModelController;
import org.rcgwt.client.ui.grid.event.IIndexesState;
import org.rcgwt.client.ui.grid.event.IModelChangeState;
import org.rcgwt.client.ui.grid.event.ModelElementChangedEvent;
import org.rcgwt.client.ui.grid.event.ModelElementChangedHandler;
import org.rcgwt.client.ui.grid.event.PropertyChangedEvent;
import org.rcgwt.client.ui.grid.event.PropertyChangedHandler;
import org.rcgwt.client.ui.grid.internal.GridModel;
import org.rcgwt.client.ui.grid.internal.SingleGroupModel;
import org.rcgwt.client.ui.grid.internal.WidgetsRegistryImpl;
import org.rcgwt.client.ui.grid.internal.model.IElementModel;
import org.rcgwt.client.ui.grid.internal.model.IGridModel;
import org.rcgwt.client.ui.grid.internal.model.ITitleCellModel;
import org.rcgwt.client.ui.grid.internal.model.ITitleRowModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.model.IDataModel;
import org.rcgwt.client.ui.grid.model.IEditableDataModel;
import org.rcgwt.client.ui.grid.model.IFiltredDataModel;
import org.rcgwt.client.ui.grid.model.IGridWidget;
import org.rcgwt.client.ui.grid.model.IGroupDataModel;
import org.rcgwt.client.ui.grid.provider.BasicAdditionalInformationProvider;
import org.rcgwt.client.ui.grid.provider.BasicCheckProvider;
import org.rcgwt.client.ui.grid.provider.BasicColumnProvider;
import org.rcgwt.client.ui.grid.provider.BasicSelectionProvider;
import org.rcgwt.client.ui.grid.provider.IAdditionalInformationProvider;
import org.rcgwt.client.ui.grid.provider.ICheckProvider;
import org.rcgwt.client.ui.grid.provider.IColumnProvider;
import org.rcgwt.client.ui.grid.provider.IFocusProvider;
import org.rcgwt.client.ui.grid.provider.IGridWidgetProvider;
import org.rcgwt.client.ui.grid.provider.IOrderProvider;
import org.rcgwt.client.ui.grid.provider.IOverProvider;
import org.rcgwt.client.ui.grid.provider.ISelectionProvider;
import org.rcgwt.client.ui.grid.reflect.IReflectedDataModel;
import org.rcgwt.client.ui.grid.renderer.IGridRenderContext;
import org.rcgwt.client.ui.grid.renderer.IGridRenderer;
import org.rcgwt.client.ui.grid.renderer.ITitleRowRenderer;
import org.rcgwt.client.ui.grid.renderer.IViewRenderContext;
import org.rcgwt.client.ui.grid.renderer.IViewRenderer;
import org.rcgwt.client.ui.grid.theme.DefaultGridTheme;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ShowRangeEvent;
import com.google.gwt.event.logical.shared.ShowRangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * 
 * 
 * @author Matthieu ROBERT
 * 
 */
public class RcGrid extends ComplexPanel implements IGridWidget {

	private static final int DEFAULT_WIDTH = 64;

	private static final int DEFAULT_HEIGHT = 64;

	// Les Providers

	private boolean selectable;

	private ISelectionProvider selectionProvider;

	private boolean checkable;

	private ICheckProvider checkProvider;

	private boolean additionalInformations;

	private IAdditionalInformationProvider additionalInformationProvider;

	private IOverProvider overProvider;

	private IFocusProvider focusProvider;

	private IOrderProvider columnsOrderProvider;

	// Sizes

	private int width;

	private int height;

	// Pagination
	private int firstRowIndex = 0;

	private int pageSize = 1;

	private int pageCount = 1;

	// Le modele MVC

	private IGridModel gridModel;

	private IGridModelController gridModelController;

	private IGridRenderer gridRenderer;

	private IGridRenderContext gridRenderContext;

	// Le dataModel
	private boolean editable;

	private IDataModel dataModel;

	private IColumnProvider columnProvider;

	private HandlerRegistration columnProviderPropertyChangeHandler;

	private IGroupDataModel groupDataModel;

	private IFiltredDataModel filtredDataModel;

	// Autres

	private State state = State.CREATED;

	private IGridTheme theme;

	private IWidgetsRegistry widgetsRegistry;

	private boolean focusAutoSelect;

	private IColumnConfig[] columnInfos;

	public RcGrid(RcGridConfig config) {
		initConfig(config);
	}

	public IDataModel getDataModel() {
		return dataModel;
	}

	protected void setState(State state) {
		Log.info("Set state to " + state);

		State oldValue = state;

		this.state = state;

		PropertyChangedEvent.fire(this, "state", oldValue, state);
	}

	public State getState() {
		return state;
	}

	private void setDataModel(IDataModel dataModel) {
		IDataModel old = this.dataModel;

		if (old == dataModel) {
			return;
		}

		this.dataModel = dataModel;

		PropertyChangedEvent.fire(this, "dataModel", old, dataModel);
	}

	public boolean isEditable() {
		return editable;
	}

	@SuppressWarnings("boxing")
	public void setEditable(boolean editable) {
		boolean old = this.editable;

		if (old == editable) {
			return;
		}

		this.editable = editable;

		PropertyChangedEvent.fire(this, "editable", old, editable);
	}

	public HandlerRegistration addBeforeSelectionHandler(
			BeforeSelectionHandler<IIndexesState> handler) {
		return getSelectionProvider().addBeforeSelectionHandler(handler);
	}

	public HandlerRegistration addSelectionHandler(
			SelectionHandler<IIndexesState> handler) {
		return getSelectionProvider().addSelectionHandler(handler);
	}

	@Override
	public HandlerRegistration addShowRangeHandler(
			ShowRangeHandler<Integer> handler) {
		return addHandler(handler, ShowRangeEvent.getType());
	}

	@Override
	protected void onAttach() {

		Log.info("onAttach ENTER");

		super.onAttach();

		if (state == State.INITIALIZED) {
			constructGrid();
		}

		render();

		Log.info("onAttach EXIT");
	}

	@Override
	public void onLoad() {

		Log.info("onLoad ENTER");

		// Create gridRenderContext if necessary
		getElement();

		super.onLoad();

		Log.info("onLoad EXIT");

	}

	@Override
	protected void onUnload() {
		if (state.isEnding()) {
			throw new IllegalStateException(
					"Unloaded has already been performed !");
		}

		state = State.DESTROYING;

		if (columnProvider != null) {

			columnProvider.setDataModel(null);

			uninstallAndInstall(columnProvider, null);

			columnProvider = null;
		}

		if (filtredDataModel != null) {
			filtredDataModel.setDataModel(null);

			uninstallAndInstall(filtredDataModel, null);

			filtredDataModel = null;
		}

		if (columnsOrderProvider != null) {
			uninstallAndInstall(columnsOrderProvider, null);

			columnsOrderProvider = null;
		}

		if (selectionProvider != null) {
			uninstallAndInstall(selectionProvider, null);

			selectionProvider = null;
		}

		if (checkProvider != null) {
			uninstallAndInstall(checkProvider, null);

			checkProvider = null;
		}

		if (additionalInformationProvider != null) {
			uninstallAndInstall(additionalInformationProvider, null);

			additionalInformationProvider = null;
		}

		if (overProvider != null) {
			uninstallAndInstall(overProvider, null);

			overProvider = null;
		}

		if (focusProvider != null) {
			uninstallAndInstall(focusProvider, null);

			focusProvider = null;
		}

		if (gridModelController != null) {
			uninstallAndInstall(gridModelController, null);

			gridModelController = null;
		}

		if (gridRenderContext != null) {
			uninstallAndInstall(gridRenderContext, null);

			gridRenderContext = null;
		}

		if (gridRenderer != null) {
			uninstallAndInstall(gridRenderer, null);

			gridRenderer = null;
		}

		if (gridModel != null) {
			uninstallAndInstall(gridModel, null);

			gridModel = null;
		}

		if (widgetsRegistry != null) {
			widgetsRegistry.removeAll();

			uninstallAndInstall(widgetsRegistry, null);

			widgetsRegistry = null;
		}

		if (theme != null) {
			uninstallAndInstall(theme, null);

			theme = null;
		}

		super.onUnload();

		state = State.DESTROYED;
	}

	public final IGroupDataModel getGroupDataModel() {
		if (groupDataModel == null) {
			setGroupDataModel(getDefaultGroupDataModel());
		}

		return groupDataModel;
	}

	private IGroupDataModel getDefaultGroupDataModel() {
		return new SingleGroupModel();
	}

	protected void setGroupDataModel(IGroupDataModel groupDataModel) {
		IGroupDataModel old = this.groupDataModel;

		if (old == groupDataModel) {
			return;
		}

		if (old != null) {
			old.setDataModel(null);
		}

		this.groupDataModel = groupDataModel;

		uninstallAndInstall(old, groupDataModel);

		if (groupDataModel != null) {
			groupDataModel.setDataModel(getDataModel());
		}

		PropertyChangedEvent.fire(this, "groupDataModel", old, groupDataModel);
	}

	public IFiltredDataModel getFiltredDataModel() {
		return filtredDataModel;
	}

	public void setFiltredDataModel(IFiltredDataModel filtredDataModel) {
		IFiltredDataModel old = this.filtredDataModel;

		if (old == filtredDataModel) {
			return;
		}

		if (old != null) {
			old.setDataModel(null);
		}

		this.filtredDataModel = filtredDataModel;

		uninstallAndInstall(old, filtredDataModel);

		if (filtredDataModel != null) {
			filtredDataModel.setDataModel(getDataModel());
		}

		PropertyChangedEvent.fire(this, "filtredDataModel", old,
				filtredDataModel);

	}

	public final ISelectionProvider getSelectionProvider() {
		return selectionProvider;
	}

	protected void setSelectionProvider(ISelectionProvider selectionProvider) {
		ISelectionProvider old = this.selectionProvider;

		if (old == selectionProvider) {
			return;
		}

		this.selectionProvider = selectionProvider;

		uninstallAndInstall(old, selectionProvider);

		PropertyChangedEvent.fire(this, "selectionProvider", old,
				selectionProvider);
	}

	protected void setColumnsOrderProvider(IOrderProvider orderProvider) {
		IOrderProvider old = this.columnsOrderProvider;

		if (old == orderProvider) {
			return;
		}

		this.columnsOrderProvider = orderProvider;

		uninstallAndInstall(old, orderProvider);

		PropertyChangedEvent.fire(this, "columnsOrderProvider", old,
				orderProvider);
	}

	protected final void uninstallAndInstall(Object oldProvider,
			Object newProvider) {

		if (oldProvider instanceof IGridWidgetProvider) {
			try {
				((IGridWidgetProvider) oldProvider).uninstall(this);

			} catch (Throwable ex) {
				Log.error("Can not uninstall grid widget provider "
						+ oldProvider, ex);
			}
		}

		if (newProvider instanceof IGridWidgetProvider) {
			try {
				((IGridWidgetProvider) newProvider).install(this);

			} catch (Throwable ex) {
				Log.error(
						"Can not install grid widget provider " + newProvider,
						ex);
			}
		}

	}

	protected ISelectionProvider createDefaultSelectionProvider() {
		return new BasicSelectionProvider();
	}

	public ICheckProvider getCheckProvider() {
		return checkProvider;
	}

	protected void setCheckProvider(ICheckProvider checkProvider) {
		ICheckProvider old = this.checkProvider;

		if (old == checkProvider) {
			return;
		}

		this.checkProvider = checkProvider;

		uninstallAndInstall(old, checkProvider);

		PropertyChangedEvent.fire(this, "checkProvider", old, checkProvider);
	}

	protected ICheckProvider createDefaultCheckProvider() {
		return new BasicCheckProvider();
	}

	public IAdditionalInformationProvider getAdditionalInformationProvider() {
		return additionalInformationProvider;
	}

	protected void setAdditionalInformationProvider(
			IAdditionalInformationProvider additionalInformationProvider) {
		IAdditionalInformationProvider old = this.additionalInformationProvider;

		if (old == additionalInformationProvider) {
			return;
		}

		this.additionalInformationProvider = additionalInformationProvider;

		uninstallAndInstall(old, additionalInformationProvider);

		PropertyChangedEvent.fire(this, "additionalInformationProvider", old,
				additionalInformationProvider);
	}

	protected IAdditionalInformationProvider createDefaultAdditionalInformationProvider() {
		return new BasicAdditionalInformationProvider();
	}

	protected void setOverProvider(IOverProvider overProvider) {
		IOverProvider old = this.overProvider;

		if (old == overProvider) {
			return;
		}

		this.overProvider = overProvider;

		uninstallAndInstall(old, overProvider);

		PropertyChangedEvent.fire(this, "overProvider", old, overProvider);
	}

	public IOverProvider getOverProvider() {
		return overProvider;
	}

	protected void setFocusProvider(IFocusProvider focusProvider) {
		IFocusProvider old = this.focusProvider;

		if (old == focusProvider) {
			return;
		}

		this.focusProvider = focusProvider;

		uninstallAndInstall(old, focusProvider);

		PropertyChangedEvent.fire(this, "focusProvider", old, focusProvider);
	}

	public IFocusProvider getFocusProvider() {
		return focusProvider;
	}

	public boolean isSelectable() {
		return selectable;
	}

	public void setSelectable(boolean selectable) {
		boolean old = this.selectable;
		if (old == selectable) {
			return;
		}

		this.selectable = selectable;

		PropertyChangedEvent.fire(this, "selectable", Boolean.valueOf(old),
				Boolean.valueOf(selectable));

		if (selectable && (getSelectionProvider() == null)) {
			setSelectionProvider(createDefaultSelectionProvider());
		}
	}

	public boolean isCheckable() {
		return checkable;
	}

	public void setCheckable(boolean checkable) {
		boolean old = this.checkable;
		if (old == checkable) {
			return;
		}

		this.checkable = checkable;

		PropertyChangedEvent.fire(this, "checkable", Boolean.valueOf(old),
				Boolean.valueOf(checkable));

		if (checkable && (getCheckProvider() == null)) {
			setCheckProvider(createDefaultCheckProvider());
		}
	}

	public boolean hasAdditionalInformations() {
		return additionalInformations;
	}

	public void setAdditionalInformations(boolean additionalInformations) {
		boolean old = this.additionalInformations;
		if (old == additionalInformations) {
			return;
		}

		this.additionalInformations = additionalInformations;

		PropertyChangedEvent.fire(this, "additionalInformations", Boolean
				.valueOf(old), Boolean.valueOf(additionalInformations));

		if (additionalInformations
				&& (getAdditionalInformationProvider() == null)) {
			setAdditionalInformationProvider(createDefaultAdditionalInformationProvider());
		}

		// Repaint
	}

	public IColumnProvider getColumnProvider() {
		return columnProvider;
	}

	private void setColumnProvider(IColumnProvider columnProvider) {

		IColumnProvider old = this.columnProvider;

		if (old == columnProvider) {
			return;
		}

		if (old != null) {
			columnProvider.setDataModel(null);
		}

		if (columnProviderPropertyChangeHandler != null) {
			columnProviderPropertyChangeHandler.removeHandler();
			columnProviderPropertyChangeHandler = null;
		}

		this.columnProvider = columnProvider;

		uninstallAndInstall(old, columnProvider);

		columnProvider.setColumnsConfig(columnInfos);

		IDataModel dataModel = getDataModel();
		if (dataModel != null) {
			columnProvider.setDataModel(getDataModel());
		}

		columnProviderPropertyChangeHandler = addPropertyChangeHandler(new PropertyChangedHandler() {

			@Override
			public void onPropertyChange(PropertyChangedEvent event) {
				if (event.getPropertyName().equals("dataModel")) {
					RcGrid.this.columnProvider.setDataModel((IDataModel) event
							.getNewValue());
				}
			}
		});

		PropertyChangedEvent.fire(this, "columnProvider", old, columnProvider);
	}

	protected IColumnProvider createDefaultColumnProvider() {

		IColumnProvider columnProvider = null;

		if (getDataModel() instanceof IReflectedDataModel<?>) {
			IReflectedDataModel<?> reflectedDataModel = (IReflectedDataModel<?>) getDataModel();

			columnProvider = reflectedDataModel.getColumnProvider();
		}

		if (columnProvider == null) {
			columnProvider = new BasicColumnProvider();
		}

		return columnProvider;
	}

	@Override
	public void onBrowserEvent(Event event) {
		super.onBrowserEvent(event);

		IGridModelController gridModelController = getGridModelController();
		if (gridModelController != null) {
			gridModelController.onBrowserEvent(this, event);
		}
	}

	public IGridModelController getGridModelController() {
		if (gridModelController != null || state.isEnding()) {
			return gridModelController;
		}

		gridModelController = getTheme().getGridModelController(getGridModel());

		uninstallAndInstall(null, gridModelController);

		return gridModelController;
	}

	protected IGridRenderer getGridRenderer() {
		if (gridRenderer != null || state.isEnding()) {
			return gridRenderer;
		}

		gridRenderer = createGridRenderer();

		uninstallAndInstall(null, gridRenderer);

		return gridRenderer;
	}

	protected IGridRenderer createGridRenderer() {
		return getTheme().getGridRenderer(getGridModel());
	}

	protected void setTheme(IGridTheme theme) {
		IGridTheme old = this.theme;

		this.theme = theme;

		uninstallAndInstall(null, theme);

		PropertyChangedEvent.fire(this, "theme", old, theme);
	}

	public IGridTheme getTheme() {
		if (theme == null) {
			theme = getDefaultTheme();

			setTheme(theme);
		}

		return theme;
	}

	protected IGridTheme getDefaultTheme() {
		return DefaultGridTheme.getTheme();
	}

	/**
	 * Adds a new child widget to the panel.
	 * 
	 * @param w
	 *            the widget to be added
	 */
	@Override
	public void add(Widget w) {
		add(w, getElement());
	}

	@SuppressWarnings("boxing")
	protected void setPageSize(int pageSize) {
		int old = this.pageSize;
		if (old == pageSize) {
			return;
		}
		this.pageSize = pageSize;

		PropertyChangedEvent.fire(this, "pageSize", old, pageSize);
	}

	public int getPageSize() {

		return pageSize;
	}

	public boolean nextPage() {
		return gotoRowIndex(firstRowIndex + pageSize);
	}

	public int getCurrentRowIndex() {
		return firstRowIndex;
	}

	public int getPageCount() {
		return pageCount;
	}

	public boolean previousPage() {
		return gotoRowIndex(firstRowIndex - pageSize);
	}

	private void updatePageSetting() {

		pageCount = dataModel.getRowCount() / pageSize
				+ ((dataModel.getRowCount() % pageSize > 0) ? 1 : 0);

		// TODO
	}

	@SuppressWarnings("boxing")
	public boolean gotoRowIndex(int rowIndex) {

		if ((rowIndex >= 0) && (rowIndex < pageCount)) {
			int start = rowIndex;
			int end = rowIndex + pageSize - 1;
			// currentPage = rowIndex;

			ShowRangeEvent.fire(this, start, end);
			/*
			 * if (pageButton != null) { pageButton.updatePagePanel(); }
			 */
			return true;
		}

		return false;
	}

	@Override
	public com.google.gwt.user.client.Element getElement() {
		if (state == State.INITIALIZED) {
			constructGrid();
		}

		return super.getElement();
	}

	protected IGridRenderContext createGridRenderContext() {
		return getGridRenderer().createRenderContext(getGridModel());
	}

	public IGridRenderContext getGridRenderContext() {
		if (gridRenderContext != null) {
			return gridRenderContext;
		}

		gridRenderContext = createGridRenderContext();

		uninstallAndInstall(null, gridRenderContext);

		return gridRenderContext;
	}

	private void constructGrid() {

		setState(State.CONSTRUCTING);

		IGridRenderContext gridRenderContext = getGridRenderContext();

		IGridRenderer gridRenderer = getGridRenderer();

		Element gridElement = gridRenderer.renderRoot(gridRenderContext,
				Document.get());

		setElement(gridElement);

		gridRenderer.postRenderRoot(gridRenderContext, gridElement);

		setState(State.CONSTRUCTED);
	}

	public IGridModel getGridModel() {
		if (gridModel != null || state.isEnding()) {
			return gridModel;
		}

		gridModel = createGridModel();

		uninstallAndInstall(null, gridModel);

		return gridModel;
	}

	protected IGridModel createGridModel() {
		IGridModel gridModel = new GridModel(this);

		return gridModel;
	}

	private void render() {
		if (state != State.CONSTRUCTED) {
			return;
		}

		setState(State.RENDERING);

		int startRowIndex = this.firstRowIndex;
		int rowCount = 0;

		IDataModel dataModel = getDataModel();
		if (dataModel != null) {
			if (pageSize < 0) {
				rowCount = dataModel.getRowCount();

			} else {
				rowCount = pageSize;
			}
		}

		renderGrid(startRowIndex, rowCount);

		if (pageSize > 0) {
			updatePageSetting();
		}

		// On initialize les evenements !
		getGridModelController();

		setState(State.RENDERED);
	}

	public void renderGrid(int startRowIndex, int rowCount) {

		IGridRenderContext gridRenderContext = getGridRenderContext();

		gridRenderContext.setRenderedRowIndex(startRowIndex, rowCount);

		getGridRenderer().render(gridRenderContext, getElement());
	}

	public void updateGrid(int startRowIndex, int rowCount) {

		IGridRenderContext gridRenderContext = getGridRenderContext();

		gridRenderContext.setRenderedRowIndex(startRowIndex, rowCount);

		getGridRenderer().update(gridRenderContext, getElement());
	}

	/**
	 * Internal call from constructor {@link RcGrid#RcGrid(RcGridConfig)}
	 * 
	 * @param config
	 */
	protected void initConfig(RcGridConfig config) {

		if (state != State.CREATED) {
			throw new IllegalStateException("Already set configuration");
		}

		setState(State.INITIALIZING);

		if (config != null) {

			IGridTheme theme = config.getTheme();

			if (theme != null) {
				setTheme(theme);
			}

			columnInfos = config.listColumnsConfig();

			width = config.getWidth();
			if (width <= 0) {
				width = getDefaultWidth();
			}

			height = config.getHeight();
			if (height <= 0) {
				height = getDefaultHeight();
			}

			focusAutoSelect = config.isFocusAutoSelect();

			// Le dataModel est en premier car il est nécessaire au
			// columnProvider
			IDataModel dataModel = config.getDataModel();
			if (dataModel != null) {
				setEditable(dataModel instanceof IEditableDataModel);

				setDataModel(dataModel);
			}

			// Column Provider
			IColumnProvider columnProvider = config.getColumnProvider();
			if (columnProvider == null) {
				columnProvider = createDefaultColumnProvider();
			}
			setColumnProvider(columnProvider);

			if (config.isSelectable()) {
				ISelectionProvider selectionProvider = config
						.getSelectionProvider();
				if (selectionProvider != null) {
					setSelectionProvider(selectionProvider);
				}

				// Si le selectionProvider = null & selectable => création d'un
				// selectionProvider par le setSelectable()

				setSelectable(true);
			}

			if (config.isCheckable()) {
				setCheckProvider(config.getCheckProvider());

				setCheckable(true);
			}

			if (config.isAdditionalInformations()) {
				setAdditionalInformationProvider(config
						.getAdditionalInformationProvider());

				setAdditionalInformations(true);
			}

			IOverProvider overProvider = config.getOverProvider();
			if (overProvider == null) {
				overProvider = theme.getOverProvider(this);
			}

			if (overProvider != null) {
				setOverProvider(overProvider);
			}

			IFocusProvider focusProvider = config.getFocusProvider();
			if (focusProvider == null) {
				focusProvider = theme.getFocusProvider(this);
			}

			if (focusProvider != null) {
				setFocusProvider(focusProvider);
			}

			IOrderProvider columnsOrderProvider = config
					.getColumnsOrderProvider();
			if (columnsOrderProvider == null) {
				columnsOrderProvider = theme.getColumnsOrderProvider(this);
			}

			if (columnsOrderProvider != null) {
				setColumnsOrderProvider(columnsOrderProvider);
			}
		}

		setState(State.INITIALIZED);
	}

	protected int getDefaultHeight() {
		return DEFAULT_WIDTH;
	}

	protected int getDefaultWidth() {
		return DEFAULT_HEIGHT;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	@Override
	public HandlerRegistration addModelElementsChangeHandler(
			ModelElementChangedHandler handler) {
		return addHandler(handler, ModelElementChangedEvent.getType());
	}

	@Override
	public HandlerRegistration addPropertyChangeHandler(
			PropertyChangedHandler handler) {
		return addHandler(handler, PropertyChangedEvent.getType());
	}

	@Override
	public HandlerRegistration addValueChangeHandler(
			ValueChangeHandler<IModelChangeState> handler) {
		return addHandler(handler, ValueChangeEvent.getType());
	}

	public boolean isGridSorted() {
		return false;
	}

	public IWidgetsRegistry getWidgetsRegistry() {
		if (widgetsRegistry != null || state.isEnding()) {
			return widgetsRegistry;
		}

		widgetsRegistry = createWidgetsRegistry();

		uninstallAndInstall(null, widgetsRegistry);

		return widgetsRegistry;
	}

	protected IWidgetsRegistry createWidgetsRegistry() {
		return new WidgetsRegistryImpl(this);
	}

	@Override
	public void setClassName(String styleName) {
		UIObject.setStyleName(getElement(), styleName);
	}

	@SuppressWarnings("incomplete-switch")
	@Override
	public void setFocus(IViewModel viewModel, IElementModel elementModel) {

		IGridRenderContext gridRenderContext = getGridRenderContext();

		IViewRenderContext viewRenderContext = gridRenderContext
				.getViewRenderContext(viewModel);

		switch (elementModel.getType()) {
		case VIEW:
		case ROW:
			IViewRenderer viewRenderer = getTheme().getViewRenderer(viewModel);

			viewRenderer.setFocus(viewRenderContext, viewModel, viewModel
					.getViewModelElements().getViewElement());

			return;

		case TITLE_CELL:
			ITitleCellModel titleCellModel = (ITitleCellModel) elementModel;

			ITitleRowModel titleRowModel = titleCellModel.getTitleRowModel();

			ITitleRowRenderer titleRowRenderer = getTheme()
					.getTitleRowRenderer(titleRowModel);

			Element titleRowElement = viewRenderContext.getViewModel()
					.getViewModelElements().getTitleRowElement(titleRowModel);

			titleRowRenderer.setFocus(viewRenderContext, titleRowModel,
					titleRowElement);

			return;
		}
	}

	@Override
	public boolean isFocusAutoSelect() {
		return focusAutoSelect;
	}

	@Override
	public IOrderProvider getColumnsOrderProvider() {
		return columnsOrderProvider;
	}

}
